﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;

namespace UnitSoftware.LINQ2SQLExtentions
{
    public class EntityManager<TEntity> where TEntity : class
    {
        readonly DataContext _dataContext;
        readonly Table<TEntity> _table;

        public event EventHandler<EntityEventArgs<TEntity>> EntityDeleted;
        public event EventHandler<EntityEventArgs<TEntity>> EntityInserted;
        public event EventHandler<EntityEventArgs<TEntity>> EntityUpdated;
        public event EventHandler<EntityOperationEventArgs<TEntity>> EntityOperation;

        public EntityManager(IDataContextFactory dataContextFactory)
            : this(dataContextFactory.CreateDataContext())
        { }

        public EntityManager(DataContext dataContext)
        {
            _dataContext = dataContext;
            _table = (Table<TEntity>) _dataContext.GetTable(typeof(TEntity));

            EntityDeleted += new EventHandler<EntityEventArgs<TEntity>>(EntityManager_EntityDeleted);
            EntityInserted += new EventHandler<EntityEventArgs<TEntity>>(EntityManager_EntityInserted);
            EntityUpdated += new EventHandler<EntityEventArgs<TEntity>>(EntityManager_EntityUpdated);
        }

        void EntityManager_EntityUpdated(object sender, EntityEventArgs<TEntity> e)
        {
            if (EntityOperation != null)
                EntityOperation(this, new EntityOperationEventArgs<TEntity>(e.Entity, Operation.Update));
        }

        void EntityManager_EntityInserted(object sender, EntityEventArgs<TEntity> e)
        {
            if (EntityOperation != null)
                EntityOperation(this, new EntityOperationEventArgs<TEntity>(e.Entity, Operation.Insert));
        }

        void EntityManager_EntityDeleted(object sender, EntityEventArgs<TEntity> e)
        {
            if (EntityOperation != null)
                EntityOperation(this, new EntityOperationEventArgs<TEntity>(e.Entity, Operation.Delete));
        }

        public void Delete(TEntity entity)
        {
            _table.DeleteOnSubmit(entity);

            _dataContext.SubmitChanges();

            if (EntityDeleted != null)
                EntityDeleted(this, new EntityEventArgs<TEntity>(entity));
        }

        public bool Known2Context(TEntity entity)
        {
            return _table.GetOriginalEntityState(entity) != null;
        }

        public void Persist(TEntity entity)
        {
            if (Known2Context(entity))
                Update(entity);
            else
                Insert(entity);
        }

        void Insert(TEntity entity)
        {
            _table.InsertOnSubmit(entity);

            _dataContext.SubmitChanges();

            if (EntityInserted != null)
                EntityInserted(this, new EntityEventArgs<TEntity>(entity));
        }

        void Update(TEntity entity)
        {
            if (_dataContext.GetChangeSet().Updates.Contains(entity))
                _dataContext.SubmitChanges();

            if (EntityUpdated != null)
                EntityUpdated(this, new EntityEventArgs<TEntity>(entity));
        }

        #region Properties

        public DataContext DataContext
        {
            get { return _dataContext; }
        }

        public Table<TEntity> Table
        {
            get { return _table; }
        }

        #endregion
    }
}
